require "logger"
require 'set'
require 'stringio'

module Wondercap
module Logging
  
  class LoggerDeviceWrapper
    attr_reader :output_devs

    def initialize(*devs)
      @output_devs = Set.new(devs)
      @output = StringIO.new
      @output_devs << @output
    end

    def method_missing(name, *args, &block)
      process_missing_method_one_time_on_each_device(Set.new, name, *args, &block)
    end

    def process_missing_method_one_time_on_each_device(processed_devices, name, *args, &block)
      processed = processed_devices.dup
      @output_devs.each do |dev| 
        next if processed.include? dev
        if dev.respond_to? :process_missing_method_one_time_on_each_device
          processed = dev.process_missing_method_one_time_on_each_device(processed, name, *args, &block)
        else
          dev.send(name, *args, &block)
          processed << dev
        end
      end
      processed
    end

    def respond_to?(name, include_private = false)
      super(name, include_private) || !@output_devs.find { |dev| !dev.respond_to?(name) }
    end

    def logger_output
      @output.string
    end

  end

  class WondercapLogger < Logger
    def wrap_method(name, str, prefix, &block)
      if str
        Logger.instance_method(name).bind(self).call(str.to_s + (prefix || "").to_s)
      else
        Logger.instance_method(name).bind(self).call(&block)
      end
    end
    private :wrap_method

    def debug(str = nil, prefix = nil, &block)
      wrap_method(:debug, str, prefix, &block)
    end

    def info(str = nil, prefix = nil, &block)
      wrap_method(:info, str, prefix, &block)
    end

    def warn(str = nil, prefix = nil, &block)
      wrap_method(:warn, str, prefix, &block)
    end

    def error(str = nil, prefix = nil, &block)
      wrap_method(:error, str, prefix, &block)
    end

    def fatal(str = nil, prefix = nil, &block)
      wrap_method(:fatal, str, prefix, &block)
    end

    def important(*args, &block)
      warn(*args, &block)
    end

    def trace(*args, &block)
      debug(*args, &block)
    end  
  end

  def logger
    @wcap_logger ||= WondercapLogger.new(logger_device)
  end

  def logger_output
    logger_device.logger_output
  end

  def logger_device
    @logger_device ||= LoggerDeviceWrapper.new(STDERR)
  end

end
end